package run

import (
	"errors"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
)

// Environ 环境变量
var Environ = os.Environ()

// Run 运行
type Run struct {
	In       *os.File
	Init     func()
	IsRead   func(read []byte)
	Process  *os.Process
	ProcAttr func(inpr, pw *os.File, dir string) *os.ProcAttr
}

// Run 启动程序
func (run *Run) Run(name string, arg ...string) (err error) {
	// 处理路径
	if filepath.Base(name) == name {
		lp, err := exec.LookPath(name)
		if err != nil {
			return err
		}
		name = lp
	}
	dir, err := filepath.Abs(filepath.Dir(name))
	if err != nil {
		return err
	}
	// 创建虚拟文件系统
	var inpr *os.File
	if run.In == nil {
		inpr, run.In, err = os.Pipe()
		if err != nil {
			return err
		}
		defer inpr.Close()
		defer run.In.Close()
	} else {
		inpr = run.In
	}
	var pw *os.File
	if run.IsRead != nil {
		var pr *os.File
		pr, pw, err = os.Pipe()
		if err != nil {
			return err
		}
		defer pr.Close()
		defer pw.Close()
		go func() {
			buffer := make([]byte, 255)
			for {
				n, err := pr.Read(buffer)
				if err == io.EOF {
					return
				}
				run.IsRead(buffer[:n])
			}
		}()
	} else {
		pw = os.Stdout
	}
	// 设置程序配置
	if run.ProcAttr == nil {
		run.ProcAttr = ProcAttr
	}
	// 执行程序
	run.Process, err = os.StartProcess(name, arg, run.ProcAttr(inpr, pw, dir))
	// 初始化通知
	if run.Init != nil {
		run.Init()
	}
	// 等待运行结束
	_, err = run.Process.Wait()
	return err
}

// Cmd cmd命令行执行
type Cmd struct {
	cmd    Run
	IsRead func(read []byte)
	run    bool
}

// WriteString 输入命令
func (cmd *Cmd) Write(name string) {
	// 是否启动
	if !cmd.run {
		return
	}
	// 去除最后换行符
	if name[len(name)-1] == '\n' {
		name = string(name[:len(name)-1])
	}
	_, _ = cmd.cmd.In.WriteString(name + "\n")
}

// Run 运行 cmd
func (cmd *Cmd) Run(run string, Init func()) error {
	if cmd.run {
		return errors.New("Cmd 已经启动")
	}
	if cmd.IsRead == nil {
		return errors.New("Cmd 回调函数不存在")
	}
	cmd.cmd = Run{}
	// 处理命令输出
	cmd.cmd.IsRead = func(read []byte) {
		cmd.IsRead(read)
	}
	// 初始化
	cmd.cmd.Init = func() {
		if runtime.GOOS == "windows" {
			// windows系统
			_, _ = cmd.cmd.In.WriteString("chcp 65001\n")
		}
		// 通知初始化回调函数
		cmd.run = true
		Init()
	}
	defer func() {
		cmd.run = false // 设置未启动
	}()
	return cmd.cmd.Run(run)
}
